// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

Widget wrap({ required Widget child, ThemeData? theme }) {
  return MaterialApp(
    theme: theme,
    home: Center(
      child: Material(child: child),
    ),
  );
}

void main() {
  testWidgets('ExpandIcon test', (WidgetTester tester) async {
    bool expanded = false;
    IconTheme iconTheme;

    // Light mode tests
    await tester.pumpWidget(wrap(
      child: ExpandIcon(
        onPressed: (bool isExpanded) {
          expanded = !expanded;
        },
      ),
    ));
    await tester.pumpAndSettle();

    expect(expanded, isFalse);
    iconTheme = tester.firstWidget(find.byType(IconTheme).last);
    expect(iconTheme.data.color, equals(Colors.black54));

    await tester.tap(find.byType(ExpandIcon));
    await tester.pumpAndSettle();
    expect(expanded, isTrue);
    iconTheme = tester.firstWidget(find.byType(IconTheme).last);
    expect(iconTheme.data.color, equals(Colors.black54));

    await tester.tap(find.byType(ExpandIcon));
    await tester.pumpAndSettle();
    expect(expanded, isFalse);
    iconTheme = tester.firstWidget(find.byType(IconTheme).last);
    expect(iconTheme.data.color, equals(Colors.black54));

    // Dark mode tests
    await tester.pumpWidget(wrap(
      child: ExpandIcon(
        onPressed: (bool isExpanded) {
          expanded = !expanded;
        },
      ),
      theme: ThemeData(brightness: Brightness.dark),
    ));
    await tester.pumpAndSettle();

    expect(expanded, isFalse);
    iconTheme = tester.firstWidget(find.byType(IconTheme).last);
    expect(iconTheme.data.color, equals(Colors.white60));

    await tester.tap(find.byType(ExpandIcon));
    await tester.pumpAndSettle();
    expect(expanded, isTrue);
    iconTheme = tester.firstWidget(find.byType(IconTheme).last);
    expect(iconTheme.data.color, equals(Colors.white60));

    await tester.tap(find.byType(ExpandIcon));
    await tester.pumpAndSettle();
    expect(expanded, isFalse);
    iconTheme = tester.firstWidget(find.byType(IconTheme).last);
    expect(iconTheme.data.color, equals(Colors.white60));
  });

  testWidgets('Material2 - ExpandIcon disabled', (WidgetTester tester) async {
    IconTheme iconTheme;
    // Test light mode.
    await tester.pumpWidget(wrap(
      theme: ThemeData(useMaterial3: false),
      child: const ExpandIcon(onPressed: null),
    ));
    await tester.pumpAndSettle();

    iconTheme = tester.firstWidget(find.byType(IconTheme).last);
    expect(iconTheme.data.color, equals(Colors.black38));

    // Test dark mode.
    await tester.pumpWidget(wrap(
      child: const ExpandIcon(onPressed: null),
      theme: ThemeData(useMaterial3: false, brightness: Brightness.dark),
    ));
    await tester.pumpAndSettle();

    iconTheme = tester.firstWidget(find.byType(IconTheme).last);
    expect(iconTheme.data.color, equals(Colors.white38));
  });

  testWidgets('Material3 - ExpandIcon disabled', (WidgetTester tester) async {
    ThemeData theme = ThemeData();
    IconTheme iconTheme;
    // Test light mode.
    await tester.pumpWidget(wrap(
      theme: theme,
      child: const ExpandIcon(onPressed: null),
    ));
    await tester.pumpAndSettle();

    iconTheme = tester.firstWidget(find.byType(IconTheme).last);
    expect(
      iconTheme.data.color,
      equals(theme.colorScheme.onSurface.withOpacity(0.38)),
    );

    theme = ThemeData(brightness: Brightness.dark);
    // Test dark mode.
    await tester.pumpWidget(wrap(
      theme: theme,
      child: const ExpandIcon(onPressed: null),
    ));
    await tester.pumpAndSettle();

    iconTheme = tester.firstWidget(find.byType(IconTheme).last);
    expect(
      iconTheme.data.color,
      equals(theme.colorScheme.onSurface.withOpacity(0.38)),
    );
  });

  testWidgets('ExpandIcon test isExpanded does not trigger callback', (WidgetTester tester) async {
    bool expanded = false;

    await tester.pumpWidget(wrap(
      child: ExpandIcon(
        onPressed: (bool isExpanded) {
          expanded = !expanded;
        },
      ),
    ));

    await tester.pumpWidget(wrap(
      child: ExpandIcon(
        isExpanded: true,
        onPressed: (bool isExpanded) {
          expanded = !expanded;
        },
      ),
    ));

    expect(expanded, isFalse);
  });

  testWidgets('ExpandIcon is rotated initially if isExpanded is true on first build', (WidgetTester tester) async {
    bool expanded = true;

    await tester.pumpWidget(wrap(
      child: ExpandIcon(
        isExpanded: expanded,
        onPressed: (bool isExpanded) {
          expanded = !isExpanded;
        },
      ),
    ));
    final RotationTransition rotation = tester.firstWidget(find.byType(RotationTransition));
    expect(rotation.turns.value, 0.5);
  });

  testWidgets('ExpandIcon default size is 24', (WidgetTester tester) async {
    final ExpandIcon expandIcon =  ExpandIcon(
      onPressed: (bool isExpanded) {},
    );

    await tester.pumpWidget(wrap(
      child: expandIcon,
    ));

    final ExpandIcon icon = tester.firstWidget(find.byWidget(expandIcon));
    expect(icon.size, 24);
  });

  testWidgets('ExpandIcon has the correct given size', (WidgetTester tester) async {
    ExpandIcon expandIcon =  ExpandIcon(
      size: 36,
      onPressed: (bool isExpanded) {},
    );

    await tester.pumpWidget(wrap(
      child: expandIcon,
    ));

    ExpandIcon icon = tester.firstWidget(find.byWidget(expandIcon));
    expect(icon.size, 36);

    expandIcon =  ExpandIcon(
      size: 48,
      onPressed: (bool isExpanded) {},
    );

    await tester.pumpWidget(wrap(
      child: expandIcon,
    ));

    icon = tester.firstWidget(find.byWidget(expandIcon));
    expect(icon.size, 48);
  });

  testWidgets('Material2 - ExpandIcon has correct semantic hints', (WidgetTester tester) async {
    final SemanticsHandle handle = tester.ensureSemantics();
    const DefaultMaterialLocalizations localizations = DefaultMaterialLocalizations();
    await tester.pumpWidget(wrap(
      theme: ThemeData(useMaterial3: false),
      child: ExpandIcon(
        isExpanded: true,
        onPressed: (bool _) { },
      ),
    ));

    expect(tester.getSemantics(find.byType(ExpandIcon)), matchesSemantics(
      hasTapAction: true,
      hasEnabledState: true,
      isEnabled: true,
      isFocusable: true,
      isButton: true,
      onTapHint: localizations.expandedIconTapHint,
    ));

    await tester.pumpWidget(wrap(
      theme: ThemeData(useMaterial3: false),
      child: ExpandIcon(
        onPressed: (bool _) { },
      ),
    ));

    expect(tester.getSemantics(find.byType(ExpandIcon)), matchesSemantics(
      hasTapAction: true,
      hasEnabledState: true,
      isEnabled: true,
      isFocusable: true,
      isButton: true,
      onTapHint: localizations.collapsedIconTapHint,
    ));
    handle.dispose();
  });

  testWidgets('Material3 - ExpandIcon has correct semantic hints', (WidgetTester tester) async {
    final SemanticsHandle handle = tester.ensureSemantics();
    const DefaultMaterialLocalizations localizations = DefaultMaterialLocalizations();

    await tester.pumpWidget(wrap(
      child: ExpandIcon(
        isExpanded: true,
        onPressed: (bool _) { },
      ),
    ));

    expect(tester.getSemantics(find.byType(ExpandIcon)), matchesSemantics(
      onTapHint: localizations.expandedIconTapHint,
      children: <Matcher>[
        matchesSemantics(
          hasTapAction: true,
          hasEnabledState: true,
          isEnabled: true,
          isFocusable: true,
          isButton: true,
        ),
      ],
    ));

    await tester.pumpWidget(wrap(
      child: ExpandIcon(
        onPressed: (bool _) { },
      ),
    ));

    expect(tester.getSemantics(find.byType(ExpandIcon)), matchesSemantics(
      onTapHint: localizations.collapsedIconTapHint,
      children: <Matcher>[
        matchesSemantics(
          hasTapAction: true,
          hasEnabledState: true,
          isEnabled: true,
          isFocusable: true,
          isButton: true,
        ),
      ],
    ));

    handle.dispose();
  });

  testWidgets('ExpandIcon uses custom icon color and expanded icon color', (WidgetTester tester) async {
    bool expanded = false;
    IconTheme iconTheme;

    await tester.pumpWidget(wrap(
      child: StatefulBuilder(
        builder: (BuildContext context, StateSetter setState) {
          return ExpandIcon(
            isExpanded: expanded,
            onPressed: (bool isExpanded) {
              setState(() {
                expanded = !isExpanded;
              });
            },
            color: Colors.indigo,
          );
        },
      ),
    ));
    await tester.pumpAndSettle();
    iconTheme = tester.firstWidget(find.byType(IconTheme).last);
    expect(iconTheme.data.color, equals(Colors.indigo));

    await tester.tap(find.byType(ExpandIcon));
    await tester.pumpAndSettle();
    iconTheme = tester.firstWidget(find.byType(IconTheme).last);
    expect(iconTheme.data.color, equals(Colors.indigo));

    expanded = false;

    await tester.pumpWidget(wrap(
      child: StatefulBuilder(
        builder: (BuildContext context, StateSetter setState) {
          return ExpandIcon(
            isExpanded: expanded,
            onPressed: (bool isExpanded) {
              setState(() {
                expanded = !isExpanded;
              });
            },
            color: Colors.indigo,
            expandedColor: Colors.teal,
          );
        },
      ),
    ));
    await tester.pumpAndSettle();
    iconTheme = tester.firstWidget(find.byType(IconTheme).last);
    expect(iconTheme.data.color, equals(Colors.indigo));

    await tester.tap(find.byType(ExpandIcon));
    await tester.pumpAndSettle();
    iconTheme = tester.firstWidget(find.byType(IconTheme).last);
    expect(iconTheme.data.color, equals(Colors.teal));

    await tester.tap(find.byType(ExpandIcon));
    await tester.pumpAndSettle();
    iconTheme = tester.firstWidget(find.byType(IconTheme).last);
    expect(iconTheme.data.color, equals(Colors.indigo));
  });

  testWidgets('ExpandIcon uses custom disabled icon color', (WidgetTester tester) async {
    IconTheme iconTheme;

    await tester.pumpWidget(wrap(
      child: const ExpandIcon(
        onPressed: null,
        disabledColor: Colors.cyan,
      ),
    ));
    await tester.pumpAndSettle();
    iconTheme = tester.firstWidget(find.byType(IconTheme).last);
    expect(iconTheme.data.color, equals(Colors.cyan));

    await tester.pumpWidget(wrap(
      child: const ExpandIcon(
        onPressed: null,
        color: Colors.indigo,
        disabledColor: Colors.cyan,
      ),
    ));
    await tester.pumpAndSettle();
    iconTheme = tester.firstWidget(find.byType(IconTheme).last);
    expect(iconTheme.data.color, equals(Colors.cyan));

    await tester.pumpWidget(wrap(
      child: const ExpandIcon(
        isExpanded: true,
        onPressed: null,
        disabledColor: Colors.cyan,
      ),
    ));
    await tester.pumpWidget(wrap(
      child: const ExpandIcon(
        isExpanded: true,
        onPressed: null,
        expandedColor: Colors.teal,
        disabledColor: Colors.cyan,
      ),
    ));
    await tester.pumpAndSettle();
    iconTheme = tester.firstWidget(find.byType(IconTheme).last);
    expect(iconTheme.data.color, equals(Colors.cyan));
  });
}
